package com.ecr.hub.database.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ecr.hub.database.dao.IDaoUser;
import com.ecr.hub.database.mapper.MapperUser;
import com.ecr.hub.model.enumeration.HubObjectActive;
import com.ecr.hub.model.enumeration.HubUserAskPassword;
import com.ecr.hub.model.enumeration.HubUserIndelible;
import com.ecr.hub.model.enumeration.HubUserRole;
import com.ecr.hub.model.user.Role;
import com.ecr.hub.model.user.User;

@Repository("daoUser")
public class DaoUserImpl extends MapperUser implements IDaoUser {

	// Récupération de tous les utilisateurs de l'application.
	private static final String REQ_SELECT_ALL = "SELECT * FROM hub_user as user, hub_user_role as user_role, "
			+ "hub_role as role WHERE user.id=user_role.id_user AND role.id=user_role.id_role "
			+ "AND user.active=" + HubObjectActive.ACTIVE.getValue();

	// Suppression d'un utilisateur.
	private static final String REQ_DELETE = "UPDATE hub_user SET active="
			+ HubObjectActive.NOT_ACTIVE.getValue() + " WHERE id=?";

	// Mise à jour d'un utilisateur.
	private static final String REQ_UPDATE = "UPDATE hub_user as user SET user.first_name=?, "
			+ "user.last_name=?, user.login=?, user.password=?, user.comment=?, "
			+ "user.new_password=? WHERE user.id=?";

	// Mise à jour du rôle pour un utilisateur.
	private static final String REQ_UPDATE_ROLE = "UPDATE hub_user_role as role SET "
			+ "role.id_role=?, role.role_user=? WHERE role.id_user=?";

	// Mise à jour connecté pour l'utilisateur.
	private static final String REQ_UPDATE_SESSION = "UPDATE hub_user SET session_id=? WHERE id=?";

	// Recherche autres utilisateurs connectés.
	private static final String REQ_SELECT_CONNECTED = REQ_SELECT_ALL
			+ " AND session_id is not null";

	// Création d'un utilisateur.
	private static final String REQ_INSERT = "INSERT INTO hub_user (last_name, first_name, login, password, "
			+ "comment, creation_date, new_password, active, indelible) VALUES (?,?,?,?,?,?,"
			+ HubUserAskPassword.ASK_PASSWORD.getValue()
			+ ","
			+ HubObjectActive.ACTIVE.getValue()
			+ ","
			+ HubUserIndelible.NOT_INDELIBLE.getValue() + ")";

	// Insertion du rôle pour un nouvel utilisateur.
	private static final String REQ_INSERT_ROLE = "INSERT INTO hub_user_role (id_user, "
			+ "login_user, role_user, id_role) VALUES (?,?,?,?)";

	// Récupération d'un utilisateur par son login et son mot de passe.
	private static final String REQ_SELECT_LOGIN_PASS = "SELECT * FROM hub_user as user, "
			+ "hub_user_role as user_role, hub_role as role WHERE user.id=user_role.id_user "
			+ "AND role.id=user_role.id_role AND user.login=? AND user.password=? AND user.active="
			+ HubObjectActive.ACTIVE.getValue();

	// Récupération d'un utilisateur par son identifiant.
	private static final String REQ_SELECT_ID = "SELECT * FROM hub_user as user, hub_user_role as user_role, "
			+ "hub_role as role WHERE user.id=user_role.id_user AND role.id=user_role.id_role AND user.id=?";

	// Mis eà jour du mot de passe pour un utilisateur.
	private static final String REQ_UPDATE_PASSWORD = "UPDATE hub_user SET password=?, new_password=? WHERE id=?";

	// Récupération de la dernière clé d'insertion.
	private static final String REQ_SELECT_LAST_ID = "SELECT LAST_INSERT_ID() FROM hub_user LIMIT 1";

	// Récupération de tous les rôles pour un utilisateur.
	private static final String REQ_SELECT_ALL_ROLES = "SELECT * FROM hub_role";

	@Autowired
	JdbcTemplate jdbcTemplate;

	/**
	 * Lecture de tous les utilisateurs, on prend tous les enregistrements de la
	 * table hub_user et de la table hub_user_role. On ne prend que les
	 * utilisateurs dont la booleen "active" est à "true".
	 */
	@Override
	public List<User> getAllList() throws DataAccessException,
			EmptyResultDataAccessException {

		return jdbcTemplate.query(REQ_SELECT_ALL, mapperUser);
	}

	/**
	 * Enregistrement d'un nouvel utilisateur, on enregistre dans la table
	 * hub_user, on recherche la dernière clé enregistrée dans le processus
	 * courant et on enregistre le role dans la table hub_user_role. On demande
	 * la ressaisie du mot de passe par l'utilisateur à la prochaine connexion.
	 * On renvoi par ailleurs l'identifiant utilisateur pour qu'il puisse être
	 * mis à jour dans le cadre de requêtes ajax.
	 */
	@Override
	@Transactional
	public int create(User user) throws DataAccessException {

		jdbcTemplate.update(
				REQ_INSERT,
				new Object[] { user.getLastName(), user.getFirstName(),
						user.getLogin(), user.getPassword(), user.getComment(),
						user.getCreationDate() });

		int idUser = jdbcTemplate.queryForInt(REQ_SELECT_LAST_ID);

		jdbcTemplate
				.update(REQ_INSERT_ROLE, new Object[] { idUser,
						user.getLogin(), user.getRole().getRole().name(),
						user.getRole().getId() });

		return idUser;
	}

	/**
	 * Modification d'un enregistrement utilisateur. Mise à jour dans la table
	 * hub_user et dans la table hub_user_role. On demande la ressaisie du mot
	 * de passe par l'utilisateur à la prochaine connexion.
	 */
	@Override
	@Transactional
	public void update(User user) throws DataAccessException {

		jdbcTemplate.update(
				REQ_UPDATE,
				new Object[] { user.getFirstName(), user.getLastName(),
						user.getLogin(), user.getPassword(), user.getComment(),
						user.isAskForPassword(), user.getId() });

		jdbcTemplate.update(REQ_UPDATE_ROLE,
				new Object[] { user.getRole().getId(),
						user.getRole().getRole().name(), user.getId() });
	}

	/**
	 * Suppression d'un utilisateur. Suppression de la ligne utilisateur dans la
	 * table hub_user et dans la table hub_user_role. On ne supprime en fait
	 * jamais les lignes, on se contente juste de passer l'indicateur "active" à
	 * "false", ceci afain de pouvoir conserver un historique sur l'ensemble des
	 * mouvements des wagons.
	 */
	@Override
	public void delete(User user) throws DataAccessException {

		jdbcTemplate.update(REQ_DELETE, new Object[] { user.getId() });
	}

	/**
	 * Récupération de la liste des rôles pour permettre l'affichage d'une liste
	 * déroulante (choix utilisateur pour le rôle).
	 */
	@Override
	public List<Role> getAllRoles() throws DataAccessException,
			EmptyResultDataAccessException {

		return jdbcTemplate.query(REQ_SELECT_ALL_ROLES, mapperRole);
	}

	/**
	 * Requête pour la recherche d'un utilisateur à partir de son login
	 * utilisateur et de son mot de passe (mire de connexion). On indique
	 * l'utilisateur comme connecté et on recherche si d'autres utilisateurs
	 * avec des droits en modification sont aussi connectés.
	 */
	@Override
	@Transactional
	public User connect(String login, String password, String sessionId)
			throws DataAccessException, EmptyResultDataAccessException {
		
		User user = jdbcTemplate.queryForObject(REQ_SELECT_LOGIN_PASS,
				mapperUser, new Object[] { login, password });

		List<User> lstUsers = jdbcTemplate.query(REQ_SELECT_CONNECTED,
				mapperUser);
		if (lstUsers.size() > 0) {
			for (User userConnected : lstUsers) {
				HubUserRole role = userConnected.getRole().getRole();
				if ((user.getId() != userConnected.getId())
						&& (role == HubUserRole.ROLE_AGENCY_CHIEF 
								|| role == HubUserRole.ROLE_COORDINATOR)) {
					user.setUsersAlreadyConnected(true);
					break;
				}
			}
		}
		user.setSessionId(sessionId);
		jdbcTemplate.update(REQ_UPDATE_SESSION,
				new Object[] { user.getSessionId(), user.getId() });
		
		return user;
	}

	/**
	 * Mise à jour du mot de passe pour l'utilisateur à la première connexion à
	 * l'application. On modifie le mot de passe dans la table hub_user et on
	 * met le booleen de demande de nouveau mot de passe à false.
	 */
	@Override
	public void updatePassword(User user) throws DataAccessException {

		jdbcTemplate.update(REQ_UPDATE_PASSWORD,
				new Object[] { user.getPassword(), user.isAskForPassword(),
						user.getId() });
	}

	/**
	 * Recherche utilisateur à partir de son identifiant.
	 */
	@Override
	public User getById(int id) throws DataAccessException,
			EmptyResultDataAccessException {

		return jdbcTemplate.queryForObject(REQ_SELECT_ID, mapperUser,
				new Object[] { id });
	}

	/**
	 * Indique si l'utilisateur est connecté ou non à l'application. Ce système
	 * empêche plusieurs utilisateurs avec les droits en modification d'êtres
	 * connectés en même temps.
	 */
	@Override
	public void disconnect(User user) throws DataAccessException {

		jdbcTemplate.update(REQ_UPDATE_SESSION,
				new Object[] { user.getSessionId(), user.getId() });

	}

	/**
	 * Retourne la liste des utilisateurs connectés à l'application.
	 */
	@Override
	public List<User> getListConnected() throws DataAccessException,
			EmptyResultDataAccessException {

		List<User> lstUsers = jdbcTemplate.query(REQ_SELECT_CONNECTED,
				mapperUser);
		if (lstUsers.size() == 0)
			throw new EmptyResultDataAccessException(0);
		return lstUsers;
	}
}